module cpu_top #(
    parameter       is_Simulation = 0)
    (
    input                  CLK_50M,
    input                  RST_n,

    // SWD
    inout      wire        SWDCLK,
    inout      wire        SWDIO,

    // UART
    input      wire        RXD,
    output     wire        TXD,

    // GPIO
    inout      wire [15:0] GPIO,

    // I2C
    inout      wire        SDA,
    output     wire        SCL
);


//---------------------------------------------------------
//CLOCK BUFFER
//---------------------------------------------------------

wire clk;
wire swclk;
generate
        if(is_Simulation) begin : SimClock

                assign swclk = SWDCLK;
                assign clk  = CLK_50M;
        
        end else begin : SynClock

                BUFG BUFG_inst(
                        .I                      (SWDCLK),
                        .O                      (swclk)
                );
                clk_wiz_0 PLL(
                        .clk_in1                (CLK_50M),
                        .resetn                 (RST_n),
                        .clk_out1               (clk)
                );
        end

endgenerate

//---------------------------------------------------------
//RESET
//---------------------------------------------------------

wire SYSRESETREQ;
reg cpuresetn;

always@(posedge clk or negedge RST_n) begin
    if(~RST_n)
        cpuresetn <= 1'b0;
    else if(SYSRESETREQ)
        cpuresetn <= 1'b0;
    else
        cpuresetn <= 1'b1;
end

wire SLEEPING;

//---------------------------------------------------------
//DEBUG IOBUF
//---------------------------------------------------------

wire SWDO;
wire SWDI;
wire SWDOEN;

generate
    if(is_Simulation) begin : SimIOBuf

        assign SWDI = SWDIO;
        assign SWDIO = (SWDOEN) ?  SWDO : 1'bz;

    end else begin : SynIOBuf

        IOBUF #(
            .DRIVE(12),             // Specify the output drive strength
            .IBUF_LOW_PWR("TRUE"),  // Low Power - "TRUE", High Performance = "FALSE" 
            .IOSTANDARD("DEFAULT"), // Specify the I/O standard
            .SLEW("SLOW")           // Specify the output slew rate
        ) SWIOBUF (
            .O(SWDI),               // Buffer output
            .IO(SWDIO),             // Buffer inout port (connect directly to top-level port)
            .I(SWDO),               // Buffer input
            .T(~SWDOEN)             // 3-state enable input, high=input, low=output
        );

    end
endgenerate

//---------------------------------------------------------
//GPIO IOBUF
//---------------------------------------------------------

wire  [15:0] GPIOIN;     // GPIO input
wire  [15:0] GPIOOUT;    // GPIO output
wire  [15:0] GPIOEN;     // GPIO output enable
genvar       i;

generate 
    for (i=0; i<16; i=i+1)
    begin : GenGPIOBuf

        IOBUF #(
            .DRIVE(12),             // Specify the output drive strength
            .IBUF_LOW_PWR("TRUE"),  // Low Power - "TRUE", High Performance = "FALSE" 
            .IOSTANDARD("DEFAULT"), // Specify the I/O standard
            .SLEW("SLOW")           // Specify the output slew rate
        ) SWIOBUF (
            .O(GPIOIN[i]),          // Buffer output
            .IO(GPIO[i]),           // Buffer inout port (connect directly to top-level port)
            .I(GPIOOUT[i]),         // Buffer input
            .T(~GPIOEN[i])          // 3-state enable input, high=input, low=output
        );

    end
endgenerate

//---------------------------------------------------------
//I2C IOBUF
//---------------------------------------------------------

wire SDAIN;
wire SDAOUTEN_n;

IOBUF #(
    .DRIVE(12),             // Specify the output drive strength
    .IBUF_LOW_PWR("TRUE"),  // Low Power - "TRUE", High Performance = "FALSE" 
    .IOSTANDARD("DEFAULT"), // Specify the I/O standard
    .SLEW("SLOW")           // Specify the output slew rate
) SWIOBUF (
    .O(SDAIN),              // Buffer output
    .IO(SDA),               // Buffer inout port (connect directly to top-level port)
    .I(1'b0),               // Buffer input
    .T(SDAOUTEN_n)          // 3-state enable input, high=input, low=output
);

//---------------------------------------------------------
//DEBUG CONFIG
//---------------------------------------------------------

reg CDBGPWRUPACK;
wire CDBGPWRUPREQ;

always @(posedge clk or negedge RST_n)begin
    if (~RST_n) 
        CDBGPWRUPACK <= 1'b0;
    else 
        CDBGPWRUPACK <= CDBGPWRUPREQ;
end

//---------------------------------------------------------
//INTERRUPT
//---------------------------------------------------------

wire [239:0] IRQ;
wire         TXINT;
wire         RXINT;
wire         TXOVRINT;
wire         RXOVRINT;
wire         UARTINT;

//---------------------------------------------------------
//CORE BUS
//---------------------------------------------------------

// CPU I-Code 
wire    [31:0]  HADDRI;
wire    [1:0]   HTRANSI;
wire    [2:0]   HSIZEI;
wire    [2:0]   HBURSTI;
wire    [3:0]   HPROTI;
wire    [31:0]  HRDATAI;
wire            HREADYI;
wire    [1:0]   HRESPI;

// CPU D-Code 
wire    [31:0]  HADDRD;
wire    [1:0]   HTRANSD;
wire    [2:0]   HSIZED;
wire    [2:0]   HBURSTD;
wire    [3:0]   HPROTD;
wire    [31:0]  HWDATAD;
wire            HWRITED;
wire    [31:0]  HRDATAD;
wire            HREADYD;
wire    [1:0]   HRESPD;
wire    [1:0]   HMASTERD;

// CPU System bus 
wire    [31:0]  HADDRS;
wire    [1:0]   HTRANSS;
wire            HWRITES;
wire    [2:0]   HSIZES;
wire    [31:0]  HWDATAS;
wire    [2:0]   HBURSTS;
wire    [3:0]   HPROTS;
wire            HREADYS;
wire    [31:0]  HRDATAS;
wire    [1:0]   HRESPS;
wire    [1:0]   HMASTERS;
wire            HMASTLOCKS;

//---------------------------------------------------------
//Cortex-M3 Core
//---------------------------------------------------------

CORTEXM3INTEGRATIONDS u_CORTEXM3INTEGRATIONDS(
    //PMU
    .ISOLATEn      (1'b1          ),
    .RETAINn       (1'b1          ),

    //RESETS
    .PORESETn      (RST_n         ),
    .SYSRESETn     (cpuresetn     ),
    .SYSRESETREQ   (SYSRESETREQ   ),
    .RSTBYPASS     (1'b0          ),
    .CGBYPASS      (1'b0          ),
    .SE            (1'b0          ),
    
    //CLOCKS
    .FCLK          (clk           ),
    .HCLK          (clk           ),
    .TRACECLKIN    (1'b0          ),

    //SYSTICK
    .STCLK         (1'b0          ),    // System Tick clock
    .STCALIB       (26'b0         ),    // System Tick calibration
    .AUXFAULT      (32'b0         ),    // Auxillary FSR pulse inputs

    //SYSTEM CONFIG
    .BIGEND        (1'b0          ),    // Static endianess select
    .DNOTITRANS    (1'b1          ),    // I/DCode arbitration control

    //SWJDAP
    .nTRST         (1'b1          ),
    .SWDITMS       (SWDI          ),
    .SWCLKTCK      (swclk         ),
    .SWDO          (SWDO          ),
    .SWDOEN        (SWDOEN        ),
    .TDI           (1'b0          ),
    .CDBGPWRUPACK  (CDBGPWRUPACK  ),
    .CDBGPWRUPREQ  (CDBGPWRUPREQ  ),

    //IRQs
    .INTISR        (IRQ           ),    // Interrupts
    .INTNMI        (1'b0          ),    // Non-maskable Interrupt

    //I-CODE BUS
    .HREADYI       (HREADYI       ),
    .HRDATAI       (HRDATAI       ),
    .HRESPI        (HRESPI        ),
    .IFLUSH        (1'b0          ),    // ICode-bus buffer flush
    .HTRANSI       (HTRANSI       ),
    .HSIZEI        (HSIZEI        ),
    .HADDRI        (HADDRI        ),
    .HBURSTI       (HBURSTI       ),
    .HPROTI        (HPROTI        ),

    //D-CODE BUS
    .HREADYD       (HREADYD       ),
    .HRDATAD       (HRDATAD       ),
    .HRESPD        (HRESPD        ),
    .EXRESPD       (1'b0          ),    // DCode-bus exclusive response
    .HMASTERD      (HMASTERD      ),
    .HTRANSD       (HTRANSD       ),
    .HSIZED        (HSIZED        ),
    .HADDRD        (HADDRD        ),
    .HBURSTD       (HBURSTD       ),
    .HPROTD        (HPROTD        ),
    .HWRITED       (HWRITED       ),
    .HWDATAD       (HWDATAD       ),

    //SYSTEM BUS
    .HREADYS       (HREADYS       ),
    .HRDATAS       (HRDATAS       ),
    .HRESPS        (HRESPS        ),
    .EXRESPS       (1'b0          ),
    .HMASTERS      (HMASTERS      ),
    .HTRANSS       (HTRANSS       ),
    .HWRITES       (HWRITES       ),
    .HSIZES        (HSIZES        ),
    .HMASTLOCKS    (HMASTLOCKS    ),
    .HADDRS        (HADDRS        ),
    .HWDATAS       (HWDATAS       ),
    .HBURSTS       (HBURSTS       ),
    .HPROTS        (HPROTS        ),

    //SLEEP
    .RXEV          (1'b0          ),
    .SLEEPHOLDREQn (1'b1          ),
    .SLEEPING      (SLEEPING      ),

    //EXTERNAL DEBUG REQUEST
    .EDBGRQ        (1'b0          ),
    .DBGRESTART    (1'b0          ),

    //DAP HMASTER override
    .FIXMASTERTYPE (1'b0          ),

    //WIC
    .WICENREQ      (1'b0          ),

    //Timestamp intereace
    .TSVALUEB      (48'b0         ),

    //CONFIG-DEBUG /Logic disable
    .MPUDISABLE    (1'b0          ),
    .DBGEN         (1'b1          ),
    .NIDEN         (1'b1         )
);

//---------------------------------------------------------
//ahb_master_mux
//---------------------------------------------------------
wire            HSELS2;
wire     [31:0] HADDRS2;
wire      [1:0] HTRANSS2;
wire      [2:0] HSIZES2;
wire            HWRITES2;
wire            HREADYS2;
wire      [2:0] HPROTS2;
wire      [2:0] HBURSTS2;
wire            HMASTLOCKS2;
wire     [31:0] HWDATAS2;
wire            HREADYOUTS2;
wire            HRESPS2;
wire     [31:0] HRDATAS2;

assign  HSELS2          =   1'b0;
assign  HADDRS2         =   32'b0;
assign  HTRANSS2        =   2'b0;
assign  HWRITES2        =   1'b0;
assign  HSIZES2         =   3'b0;
assign  HWDATAS2        =   32'b0;
assign  HBURSTS2        =   3'b0;
assign  HPROTS2         =   3'b0;
assign  HMASTLOCKS2     =   1'b0;

wire            HSEL_SRAM;
wire     [31:0] HADDR_SRAM;
wire      [1:0] HTRANS_SRAM;
wire      [2:0] HSIZE_SRAM;
wire            HWRITE_SRAM;
wire            HREADY_SRAM;
wire      [2:0] HPROT_SRAM;
wire      [2:0] HBURST_SRAM;
wire            HMASTLOCK_SRAM;
wire     [31:0] HWDATA_SRAM;
wire            HREADYOUT_SRAM;
wire            HRESP_SRAM;
wire     [31:0] HRDATA_SRAM;

cmsdk_ahb_master_mux
#(
    .PORT0_ENABLE (1'b1 ),
    .PORT1_ENABLE (1'b1 ),
    .PORT2_ENABLE (1'b0 ),
    .DW           (32   )
)
u_cmsdk_ahb_master_mux(
    .HCLK        (clk         ),
    .HRESETn     (cpuresetn   ),

    .HSELS0      (1'b1        ),
    .HADDRS0     (HADDRI      ),
    .HTRANSS0    (HTRANSI     ),
    .HSIZES0     (HSIZEI      ),
    .HWRITES0    (1'b0        ),
    .HREADYS0    (HREADYI     ),
    .HPROTS0     (HPROTI      ),
    .HBURSTS0    (HBURSTI     ),
    .HMASTLOCKS0 (1'b0        ),
    .HWDATAS0    (32'b0       ),
    .HREADYOUTS0 (HREADYI     ),//?
    .HRESPS0     (HRESPI      ),
    .HRDATAS0    (HRDATAI     ),

    .HSELS1      (1'b1        ),
    .HADDRS1     (HADDRD     ),
    .HTRANSS1    (HTRANSD    ),
    .HSIZES1     (HSIZED     ),
    .HWRITES1    (HWRITED    ),
    .HREADYS1    (HREADYD    ),
    .HPROTS1     (HPROTD     ),
    .HBURSTS1    (HBURSTD    ),
    .HMASTLOCKS1 (1'b0       ),
    .HWDATAS1    (HWDATAD    ),
    .HREADYOUTS1 (HREADYD    ),
    .HRESPS1     (HRESPD     ),
    .HRDATAS1    (HRDATAD    ),

    .HSELS2      (HSELS2      ),
    .HADDRS2     (HADDRS2     ),
    .HTRANSS2    (HTRANSS2    ),
    .HSIZES2     (HSIZES2     ),
    .HWRITES2    (HWRITES2    ),
    .HREADYS2    (HREADYOUTS2 ),////////////////////////
    .HPROTS2     (HPROTS2     ),
    .HBURSTS2    (HBURSTS2    ),
    .HMASTLOCKS2 (HMASTLOCKS2 ),
    .HWDATAS2    (HWDATAS2    ),
    .HREADYOUTS2 (HREADYOUTS2 ),
    .HRESPS2     (HRESPS2     ),
    .HRDATAS2    (HRDATAS2    ),

    .HSELM       (HSEL_SRAM       ),
    .HADDRM      (HADDR_SRAM      ),
    .HTRANSM     (HTRANS_SRAM     ),
    .HSIZEM      (HSIZE_SRAM      ),
    .HWRITEM     (HWRITE_SRAM     ),
    .HREADYM     (HREADY_SRAM     ),
    .HPROTM      (HPROT_SRAM      ),
    .HBURSTM     (HBURST_SRAM     ),
    .HMASTLOCKM  (HMASTLOCK_SRAM  ),
    .HWDATAM     (HWDATA_SRAM     ),
    .HREADYOUTM  (HREADYOUT_SRAM  ),
    .HRESPM      (HRESP_SRAM      ),
    .HRDATAM     (HRDATA_SRAM     ),
    .HMASTERM    (HMASTER_SRAM    )
);

//---------------------------------------------------------
//SRAM
//---------------------------------------------------------

wire [31:0] SRAMRDATA;
wire [13:0] SRAMADDR;
wire  [3:0] SRAMWREN;
wire [31:0] SRAMWDATA;
wire        SRAMCS;

cmsdk_ahb_to_sram 
#(
    .AW (16 )
)
u_ahb_to_ITCM(
    .HCLK      (clk       ),
    .HRESETn   (cpuresetn ),

    .HSEL      (HSEL_SRAM     ),
    .HREADY    (HREADYOUT_SRAM),////////////
    .HTRANS    (HTRANS_SRAM   ),
    .HSIZE     (HSIZE_SRAM    ),
    .HWRITE    (HWRITE_SRAM   ),
    .HADDR     (HADDR_SRAM    ),
    .HWDATA    (HWDATA_SRAM   ),
    .HREADYOUT (HREADYOUT_SRAM),
    .HRESP     (HRESP_SRAM    ),
    .HRDATA    (HRDATA_SRAM   ),

    .SRAMRDATA (SRAMRDATA ),
    .SRAMADDR  (SRAMADDR  ),
    .SRAMWEN   (SRAMWREN   ),
    .SRAMWDATA (SRAMWDATA ),
    .SRAMCS    (SRAMCS    )
);

cmsdk_fpga_sram 
#(
    .AW       (14       )
)
u_ITCM(
    .CLK   (clk       ),
    .ADDR  (SRAMADDR  ),
    .WDATA (SRAMWDATA ),
    .WREN  (SRAMWREN  ),
    .CS    (SRAMCS    ),
    .RDATA (SRAMRDATA )
);

//---------------------------------------------------------
//ahb_subsystem
//---------------------------------------------------------

wire TXEN;

wire [15:0] GPIOINT;
wire        COMBINT;

ahb_sub_system u_ahb_sub_system(
    .HCLK      (clk       ),
    .HRESETn   (cpuresetn ),

    .HADDR     (HADDRS    ),
    .HTRANS    (HTRANSS   ),
    .HSIZE     (HSIZES    ),
    .HPROT     (HPROTS    ),
    .HWRITE    (HWRITES   ),
    .HREADY    (HREADYS   ),
    .HWDATA    (HWDATAS   ),
    .HREADYOUT (HREADYS   ),
    .HRDATA    (HRDATAS   ),
    .HRESP     (HRESPS    ),
    // UART signal
    .RXD       (RXD       ),
    .TXD       (TXD       ),
    .TXEN      (TXEN      ),
    .TXINT     (TXINT     ),
    .RXINT     (RXINT     ),
    .TXOVRINT  (TXOVRINT  ),
    .RXOVRINT  (RXOVRINT  ),
    .UARTINT   (UARTINT   ),
    // GPIO signal
    .GPIOIN    (GPIOIN    ),    // GPIO input
    .GPIOOUT   (GPIOOUT   ),    // GPIO output
    .GPIOEN    (GPIOEN    ),    // GPIO output enable
    .GPIOINT   (GPIOINT   ),    // Interrupt output for each pin
    .COMBINT   (COMBINT   ),    // Combined interrupt
    // I2C signal
    .SDAIN     (SDAIN     ),
    .SCL       (SCL       ),
    .SDAOUTEN_n(SDAOUTEN_n),
    // Timer signal
    .TIMERINT  (TIMERINT  )
);

//---------------------------------------------------------
//INTERRUPT
//---------------------------------------------------------

assign IRQ = {236'b0,TIMERINT,TXOVRINT|RXOVRINT,TXINT,RXINT};

endmodule